﻿/*///////////////////////////////////////////////////////////
TodoAppState.as
Ben Smith
2009
///////////////////////////////////////////////////////////*/

package com.bsmith.app.model 
{
	import com.bsmith.app.presentation.TodoEntry;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.net.SharedObject;
	import flash.utils.Dictionary;
	
	/*===========================================================
	TodoAppState
	State of the todo app at any given time
	===========================================================*/
	public class TodoAppState extends EventDispatcher
	{
		static public const TODO_ACTIVE:String = "ActiveItems";
		static public const TODO_COMPLETED:String = "CompletedItems";
		
		static public const EVENT_CHANGED:String = "tas.chg";
		
		static private const SAVE_DATA:String = "TodoApp-TodoAppState";
		
		static private var _cInstance:TodoAppState = null;
		static private const _aRequiredLists:Array = [TODO_ACTIVE, TODO_COMPLETED];
	
		private var _currTodoId:uint = 0;							//Current todo id
		private var _dctTodoItemLists:Dictionary = null;			//Dictionary of item lists
		private var _dctTodoItemListCallbacks:Dictionary = null;	//Queued callbacks for item lists
		
		/*-----------------------------------------------------------
		TodoAppState - ctor
		-----------------------------------------------------------*/
		public function TodoAppState(lock:TodoAppStateLock) 
		{
			_dctTodoItemLists = new Dictionary;
			_dctTodoItemListCallbacks = new Dictionary;
		}
		
		/*-----------------------------------------------------------
		initialize
		Initializes app state
		-----------------------------------------------------------*/
		public function initialize()
		{
			//load();
			for each (var sListType:String in _aRequiredLists)
			{
				if (!_dctTodoItemLists[sListType])
					addTodoItemList(new TodoItemList(sListType));
			}
		}
		
		/*-----------------------------------------------------------
		serialize
		Serializes state to xml
		-----------------------------------------------------------*/
		private function serialize():XML
		{
			var xml:XML = new XML("<Todo/>");
			xml.TodoLists = new XML;
			for each (var list:TodoItemList in _dctTodoItemLists)
				xml.TodoLists.appendChild(list.serialize());
			xml.@currId = _currTodoId;
			return xml;
		}
		
		/*-----------------------------------------------------------
		deserialize
		Deserializes state from xml
		-----------------------------------------------------------*/
		private function deserialize(xml:XML)
		{
			_dctTodoItemLists = new Dictionary;
			for each (var xmlList:XML in xml.TodoLists.*)
			{
				var list:TodoItemList = new TodoItemList(xmlList.name());
				addTodoItemList(list);
				list.deserialize(xmlList);
			}
			_currTodoId = xml.@currId;
		}
		
		/*-----------------------------------------------------------
		save
		Saves app state
		-----------------------------------------------------------*/
		private function save():void
		{
			var so:SharedObject = SharedObject.getLocal(SAVE_DATA);
			so.data.xml = serialize();
			so.flush();
		}
		
		/*-----------------------------------------------------------
		load
		Loads app state
		-----------------------------------------------------------*/
		private function load():void
		{
			var so:SharedObject = SharedObject.getLocal(SAVE_DATA);
			var xml:XML = XML(so.data.xml);
			deserialize(xml);
		}
		
		/*-----------------------------------------------------------
		get
		Returns the instance of the singleton
		-----------------------------------------------------------*/
		static public function get instance() : TodoAppState
		{
			if (!_cInstance)
				_cInstance = new TodoAppState(new TodoAppStateLock);
			return _cInstance;
		}
		
		/*-----------------------------------------------------------
		addTodoItemList
		Adds a new todo item list to the app state
		-----------------------------------------------------------*/
		private function addTodoItemList(list:TodoItemList)
		{
			list.addEventListener(TodoItemList.EVENT_CHANGED, onListChanged, false, 0, true);		
			_dctTodoItemLists[list.getType()] = list;
			if (_dctTodoItemListCallbacks[list.getType()])
			{
				for each (var callbackSettings:Object in _dctTodoItemListCallbacks[list.getType()])
					list.addEventListener(callbackSettings.type, callbackSettings.listener, callbackSettings.useCapture, callbackSettings.priority, callbackSettings.useWeakReference);
				delete _dctTodoItemListCallbacks[list.getType()];
			}
		}
		
		/*-----------------------------------------------------------
		onListChanged
		Save state whenever a list changes
		-----------------------------------------------------------*/
		private function onListChanged(e:Event):void 
		{
			save();
			this.dispatchEvent(new Event(EVENT_CHANGED));
		}
		
		/*-----------------------------------------------------------
		addTodoListEventCallback
		Adds an event listener for todo lists.  This is a safe
		operation, because if the list does not exist, the callback
		settings will be queued up until the list is created
		-----------------------------------------------------------*/
		public function addTodoListEventCallback(sListType:String, type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false)
		{
			if (_dctTodoItemLists[sListType])
				_dctTodoItemLists[sListType].addEventListener(type, listener, useCapture, priority, useWeakReference);
			else
				queueTodoListEventCallback(sListType, type, listener, useCapture, priority, useWeakReference);
		}
		
		/*-----------------------------------------------------------
		queueTodoListEventCallback
		Queues a todo list callback until its associated list is
		created
		-----------------------------------------------------------*/
		private function queueTodoListEventCallback(sListType:String, type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false)
		{
			if (!_dctTodoItemListCallbacks[sListType])
				_dctTodoItemListCallbacks[sListType] = new Array;
			_dctTodoItemListCallbacks[sListType].push( {type:type, listener:listener, useCapture:useCapture, useWeakReference:useWeakReference} );
		}
		
		/*###########################################################
		GETTERS AND SETTERS
		###########################################################*/
		public function getTodoList(sType:String):TodoItemList { return _dctTodoItemLists[sType]; }
		public function getNextTodoId():uint { return _currTodoId++; save(); }
		
	}
}

class TodoAppStateLock {}